$import( "SmitearWebGL.Objects.Mesh" );
$import( "SmitearWebGL.Core.Geometry.InputDataSet" );
$import( "SmitearWebGL.Core.Geometry.VertexDataSet" );

SmitearWebGL.Objects.Primitive.TorusKnot = SmitearWebGL.Objects.Mesh.extend(
{
	/* * * * * * * * constructor * * * * * * * */
	init : function()
	{
		this._super();
		this._registClass( "SmitearWebGL.Objects.Primitive.TorusKnot" );
	}
	,
	_build : function( segmentsR, segmentsT, radius, heightScale, tube )
	{
		var _p = 2.0;
		var _q = 3.0;

		var i, j, k = 0, m = 0;
		var tang = $V3();
		var n = $V3();
		var bitan = $V3();

		var vertices = new Array( segmentsR * segmentsT * 3 );
		var uvs = new Array( segmentsR * segmentsT * 6 * 3 );
		var indices = new Array( segmentsR * segmentsT * 6 );
		
		for ( i = 0; i < segmentsR; ++ i )
		{
			for ( j = 0; j < segmentsT; ++ j, k += 3 )
			{
				var u = i / segmentsR * 2 * _p * Math.PI;
				var v = j / segmentsT * 2 * Math.PI;
				var p = this._getPos( u, v, _q, _p, radius, heightScale );
				var p2 = this._getPos( u + .01, v, _q, _p, radius, heightScale );
				var cx, cy;

				tang.x = p2.x - p.x;
				tang.y = p2.y - p.y;
				tang.z = p2.z - p.z;
				n.x = p2.x + p.x;
				n.y = p2.y + p.y;
				n.z = p2.z + p.z;
				SmitearWebGL.Core.Math.Vector3.cross( tang, n, bitan );
				SmitearWebGL.Core.Math.Vector3.cross( bitan, tang, n );
				bitan.normalize();
				n.normalize();

				cx = tube * Math.cos( v );
				cy = tube * Math.sin( v );
				p.x += cx * n.x + cy * bitan.x;
				p.y += cx * n.y + cy * bitan.y;
				p.z += cx * n.z + cy * bitan.z;
					
				vertices[k+0] = p.x;
				vertices[k+1] = p.y;
				vertices[k+2] = p.z;
			}
		}

		k = 0;
		var ip, jp;
		var uva = new Array( 2 );
		var uvb = new Array( 2 );
		var uvc = new Array( 2 );
		var uvd = new Array( 2 );
		
		for ( i = 0; i < segmentsR; ++ i )
		{
			for ( j = 0; j < segmentsT; ++ j )
			{
				var ip = ( i + 1 ) % segmentsR;
				var jp = ( j + 1 ) % segmentsT;
					
				indices[k + 0] = i * segmentsT + j;
				indices[k + 1] = i * segmentsT + jp;
				indices[k + 2] = ip * segmentsT + j;
				indices[k + 3] = ip * segmentsT + jp;
				indices[k + 4] = ip * segmentsT + j;
				indices[k + 5] = i * segmentsT + jp;
				
				uva[0] = i / segmentsR;
				uva[1] = j / segmentsT;
				uvb[0] = ( i + 1 ) / segmentsR;
				uvb[1] = j / segmentsT;
				uvc[0] = i / segmentsR;
				uvc[1] = ( j + 1 ) / segmentsT;
				uvd[0] = ( i + 1 ) / segmentsR;
				uvd[1] = ( j + 1 ) / segmentsT;
				
				uvs[k+0] = uva[0];
				uvs[k+1] = uva[1];
				uvs[k+2] = 0.0;
				uvs[k+3] = uvb[0];
				uvs[k+4] = uvb[1];
				uvs[k+5] = 0.0;
				uvs[k+6] = uvc[0];
				uvs[k+7] = uvc[1];
				uvs[k+8] = 0.0;
				uvs[k+9] = uvd[0];
				uvs[k+10] = uvd[1];
				uvs[k+11] = 0.0;
				uvs[k+12] = uvc[0];
				uvs[k+13] = uvc[1];
				uvs[k+14] = 0.0;
				uvs[k+15] = uvb[0];
				uvs[k+16] = uvb[1];
				uvs[k+17] = 0.0;
				
				m += 6;
				k += 18;
			}
		}
		
		this._vertices = vertices;
		this._indices = indices;
			
		var inputDatas = [
			new SmitearWebGL.Core.Geometry.InputDataSet( indices, vertices, 3 ),
			new SmitearWebGL.Core.Geometry.InputDataSet( indices, uvs, 3 )
		];
		
		var vOutputData = SmitearWebGL.Core.Geometry.Geometry.buildRawData( inputDatas );
		
		var vAdj = SmitearWebGL.Core.Geometry.Geometry.buildVertexAdjancency( inputDatas[0] );
		var fNormals = SmitearWebGL.Core.Geometry.Geometry.buildFaceNormal( inputDatas[0] );
		var nOutpuData = SmitearWebGL.Core.Geometry.Geometry.buildVertexNormal( inputDatas[0], vAdj, fNormals, new SmitearWebGL.Core.Geometry.InputDataSet( vOutputData.indices, null, 3 ) );
		
		var rawData = SmitearWebGL.Core.Geometry.Geometry.combineVertexDatas( [
			new SmitearWebGL.Core.Geometry.VertexDataSet( vOutputData.data, 6 ),
			new SmitearWebGL.Core.Geometry.VertexDataSet( nOutpuData.data, 3 ),
		] );
		
		this._boundingBox = SmitearWebGL.Core.Geometry.Geometry.computeBoundingBoxByRawVertices( vertices );
		
		var vertDescs = [
			new SmitearWebGL.Resources.VertexElementDescription( this.context.gl, 3, 36, 0, SmitearWebGL.Resources.Buffer.DataType.FLOAT, SmitearWebGL.Resources.Buffer.AttribLocation.POSITION ),
			new SmitearWebGL.Resources.VertexElementDescription( this.context.gl, 3, 36, 12, SmitearWebGL.Resources.Buffer.DataType.FLOAT, SmitearWebGL.Resources.Buffer.AttribLocation.TEXCOORD ),
			new SmitearWebGL.Resources.VertexElementDescription( this.context.gl, 3, 36, 24, SmitearWebGL.Resources.Buffer.DataType.FLOAT, SmitearWebGL.Resources.Buffer.AttribLocation.NORMAL )
		]
		
		var idxDescs = [
			new SmitearWebGL.Resources.IndexElementDescription( this.context.gl, vOutputData.indices.length, 0, SmitearWebGL.Resources.Buffer.DataType.UNSIGNED_SHORT, SmitearWebGL.Resources.IndexBuffer.DrawType.TRIANGLES )
		]
		
		this.createVertexBuffer( rawData, vertDescs );
		this.createIndexBuffer( vOutputData.indices, idxDescs );
	}
	,
	_getPos : function( u, v, q, p, radius, heightScale )
	{
		var cu = Math.cos( u );
		var cv = Math.cos( v );
		var su = Math.sin( u );
		var quOverP = q / p * u;
		var cs = Math.cos( quOverP );
		var pos = $V3();

		pos.x = radius * ( 2 + cs ) * .5 * cu;
		pos.y = radius * ( 2 + cs ) * su * .5;
		pos.z = heightScale * radius * Math.sin( quOverP ) * .5;

		return pos;
	}
	,
	_createResource : function( params )
	{
		this._super();
		
		if ( ! params )
		{
			params = {};
			params.segmentsR = 24;
			params.segmentsT = 16;
			params.radius = 1.0;
			params.heightScale = 1.0;
			params.tube = 0.2;
		}
		
		this._build( params.segmentsR, params.segmentsT, params.radius, params.heightScale, params.tube );
	}
}
);